package core

import "strings"

type ObjectType string

const (
	NULL_OBJ      = "NULL_OBJ"
	BOOLEAN_OBJ   = "BOOLEAN_OBJ"
	SYMBOL_OBJ    = "SYMBOL_OBJ"
	PROCEDURE_OBJ = "PROCEDURE_OBJ"
	BUILTIN_OBJ   = "BUILTIN"
	ERROR_OBJ     = "ERROR_OBJ"
)

// Object represents a value in the Scheme environment
// It's the interface that all Scheme values must implement
type Object interface {
	// Type returns the type of the object
	Type() ObjectType

	// Inspect returns a string representation of the object
	Inspect() string
}

type NullObject struct{}

func (n NullObject) Type() ObjectType {
	return NULL_OBJ
}

func (n NullObject) Inspect() string {
	return ""
}

var NULL_OBJECT = NullObject{}

type BooleanObject struct {
	Value bool
}

func (b *BooleanObject) Type() ObjectType {
	return BOOLEAN_OBJ
}

func (b *BooleanObject) Inspect() string {
	if b.Value {
		return "#t"
	}
	return "#f"
}

type SymbolObject struct {
	Value string
}

func (s *SymbolObject) Type() ObjectType {
	return SYMBOL_OBJ
}

func (s *SymbolObject) Inspect() string {
	return s.Value
}

type Procedure struct {
	Name   string
	Params []string
	Body   []Expression
	Env    *Environment
}

// Type returns the object type as a string
func (p *Procedure) Type() ObjectType {
	return PROCEDURE_OBJ
}

// String returns the string representation of the procedure
func (p *Procedure) Inspect() string {
	var s strings.Builder

	s.WriteString("(procedure ")
	s.WriteString(p.Name)
	if len(p.Params) > 0 {
		s.WriteString("(")
		for i, param := range p.Params {
			s.WriteString(param)
			if i < len(p.Params)-1 {
				s.WriteString(" ")
			}
		}
		s.WriteString(")")
	}
	s.WriteString(")")
	return s.String()
}
